package com.javasm.mb_store_api_final.ordersys.util;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.baomidou.mybatisplus.extension.service.IService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;


@Component
public class ExportUtil<T>{

    @Resource
    HttpServletResponse response;

    /**
     * @param file 文件对象
     * @param tClass 存储数据泛型
     * @param service 执行保存方法的Service
     * @throws IOException
     */
    public void readExcel(MultipartFile file, Class<T> tClass, IService<T> service) throws IOException {

        EasyExcel.read(file.getInputStream(), tClass, new ReadListener<T>() {
            //设定,每次存储1000条数据
            private static final int SAVESIZE = 1000;
            //存储的集合
            private List<T> saveList = ListUtils.newArrayListWithExpectedSize(SAVESIZE);
            @Override
            public void invoke(T t, AnalysisContext analysisContext) {
                //每读取一条数据,都会执行当前的方法
                saveList.add(t);
                if (saveList.size() >= SAVESIZE){
                    //保存一次
                    saveData();
                    //清空集合
                    saveList = ListUtils.newArrayListWithExpectedSize(SAVESIZE);
                }
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                //在所有的数据都处理完成之后,最后执行的一个方法
                saveData();
            }

            private void  saveData(){
                //把 临时 存储的数据 保存到数据库
                service.saveBatch(saveList);
            }
        }).sheet().doRead();
    }

    /**
     * @param fileName 导出的文件名
     * @param list List数据
     * @param tClass List泛型
     * @throws IOException
     */
    public void expExcel(String fileName, List<T> list, Class<T> tClass) throws IOException {
        try {
            // 设置响应头
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            // 这里URLEncoder.encode可以防止中文乱码
            fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

            // 使用EasyExcel写入数据
            EasyExcel.write(response.getOutputStream(), tClass)
                    .sheet(fileName)
                    .doWrite(list);
        } catch (IOException e) {
            // 处理异常，例如记录日志或抛出自定义异常
            throw new RuntimeException("导出Excel失败", e);
        } finally {
            // 关闭输出流
            response.getOutputStream().close();
        }
    }

    /**
     *
     * @param fileName 导出的文件名
     * @param templateName 使用的模版名称
     * @param dataModel 导出的数据
     * @throws IOException
     * @throws TemplateException
     */
    public void expWord(String fileName, String templateName, Map<String, Object> dataModel) throws IOException, TemplateException {
        // 设置响应头
        response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        response.setCharacterEncoding("UTF-8");
        // 这里URLEncoder.encode可以防止中文乱码
        fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".docx");

        // 配置 Freemarker
        //创建一个Configuration对象，并使用当前版本号进行初始化。
        Configuration configuration = new Configuration(Configuration.VERSION_2_3_30);
        //设置默认编码为UTF-8。
        configuration.setDefaultEncoding("UTF-8");
        //设置模板异常处理器为RETHROW_HANDLER，即抛出异常时会直接向上抛出。
        configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
        //禁止在日志中输出模板异常。
        configuration.setLogTemplateExceptions(false);
        //开启自动包装未检查异常，使得在模板中抛出的RuntimeException和Error会被包装成TemplateException。
        configuration.setWrapUncheckedExceptions(true);
        //禁止在循环变量为空时使用默认值
        configuration.setFallbackOnNullLoopVariable(false);

        configuration.setClassForTemplateLoading(this.getClass(), "/docs");

        // 加载模板
        Template template = configuration.getTemplate(templateName);

        // 输出到 response.getOutputStream()
        Writer out = new OutputStreamWriter(response.getOutputStream(), "UTF-8");
        template.process(dataModel, out);
        out.flush();
        out.close();
    }

    public ResponseEntity<InputStreamResource> downloadFile(String filename) throws IOException {
        // 获取文件路径
        Path path = Paths.get("rivergod-admin-api/src/main/resources/static/", filename);
        // 创建文件输入流
        InputStream fileStream = Files.newInputStream(path);

        // 创建InputStreamResource，它将文件流包装为可传输的资源
        InputStreamResource resource = new InputStreamResource(fileStream);

        // 设置响应头，指定文件名和下载行为
        String encodedFilename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
        HttpHeaders headers = new HttpHeaders();
        headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + encodedFilename);
        headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_OCTET_STREAM_VALUE);

        // 返回包含文件流的ResponseEntity
        return ResponseEntity.ok()
                .headers(headers)
                .contentLength(Files.size(path))
                .body(resource);
    }

}
